<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>饿了么黑白模式切换</title>
    <style>
      :root {
        --bg-color: #fff;
        background-color: var(--bg-color);
      }

      body {
        margin: 0;
        padding: 0;
        height: 100vh;
      }

      :root.dark {
        --bg-color: #333;
      }

      ::view-transition-old(root),
      ::view-transition-new(root) {
        animation: none;
        mix-blend-mode: normal;
      }

      /* 这步很重要，会影响过渡效果生不生效 */
      ::view-transition-old(root) {
        z-index: 2147483646;
      }

      ::view-transition-new(root) {
        z-index: 1;
      }

      .dark::view-transition-new(root) {
        z-index: 2147483646;
      }

      .dark::view-transition-old(root) {
        z-index: 1;
      }
    </style>
  </head>
  <body>
    <button>切换模式</button>

    <script>
      const btn = document.querySelector("button");

      btn.addEventListener("click", (e) => {
        const { clientX, clientY } = e;
        /** 当前类名是否包含dark */
        const isDark = document.documentElement.classList.contains("dark");

        /** startViewTransition() 方法开始一个新的视图过渡 */
        const transition = document.startViewTransition(() => {
          // 当前页面的屏幕截图后执行
          document.documentElement.classList.toggle("dark");
        });

        /** 会在伪元素树被创建且过渡动画即将开始时兑现 */
        transition.ready.then(() => {
          /** 计算圆的半径
           * Math.hypot() 计算三角形的斜边长
           * 因为画出的圆需要大于整个视口,三角形的斜边绝对大于直角边的,
           * 所以用来当作圆的半径的话,圆肯定大于视口;
           *  */
          const radius = Math.hypot(
            Math.max(clientX, window.innerWidth - clientX), // 直角三角形的直角边1
            Math.max(clientY, window.innerHeight - clientY) // 直角三角形的直角边2
          );

          /** 给::view-transition-new(app),::view-transition-old(app) 为元素添加动画 */

          const clipPath = [
            `circle(0px at ${clientX}px ${clientY}px)`,
            `circle(${radius}px at ${clientX}px ${clientY}px)`,
          ];

          document.documentElement.animate(
            {
              clipPath: isDark ? [...clipPath].reverse() : clipPath,
            },
            {
              duration: 400,
              easing: "ease-in",
              pseudoElement: isDark
                ? "::view-transition-old(root)"
                : "::view-transition-new(root)", // 将动画应用到伪元素
            }
          );
        });
      });
    </script>
  </body>
</html>
